/**
  ETFAna project, Anyang Normal University && IMP-CAS
  \class ETFAnalyzer
  \brief abstract base class for tasks in the tracking stage
  \author SUN Yazhou, asia.rabbit@163.com
  \since 2021-10-04
  \date 2024-06-19 last modified
  \attention
  changelog
  <table>
  <tr>  <th>Date         <th>Author      <th>Description                   </tr>
  <tr>  <td>2021-10-04   <td>Asia Sun    <td>file created                  </tr>
  </table>

  \copyright Copyright (c) 2021-2024 Anyang Normal U. && IMP-CAS with LGPLv3 LICENSE
*/

#include <iostream>
#include <iomanip>
#include <TTree.h>
#include <TClonesArray.h>
#include "ETFAnalyzer.h"
#include "ETFDetectorManager.h"
#include "ETFTrackManager.h"
#include "ETFPIDManager.h"
#include "ETFParaManager.h"
#include "ETFRun.h"
#include "ETFPID.h"
#include "ETFPIDm.h"
#include "ETFPXISection.h"
#include "ETFPXIFragment.h"
#include "ETFVMEEvent.h"
#include "ETFVisual.h"
#include "ETFOnlineSource.h"
#include "ETFPXIUnpacker.h"
#include "ETFVMEUnpacker.h"
#include "ETFRawChannel.h"
#include "ETFPostMagTracker.h"
#include "ETFOnlineCache.h"
#include "ETFDaqData.h"
#include "ETFQueue.h"
#include "ETFAuxAna.h"
#include "ETFMsg.h"

using std::cout;
using std::endl;
using std::flush;
using std::setw;
using std::ios_base;

ETFAnalyzer::ETFAnalyzer() : fRun(0), fpm(0), fdm(0), fAuxAna(0), ftm(0), fpidm(0),
fPID(0), fPIDm(0), fVis(0){
  fIsPIDm = false;
  fDetDebug = false; fTrackingDebug = false; fPIDDebug = false;
  fIs3DTracking = false; fAnaLevel = -2; fPhiAvrg = -999.;
  fIndex = fCntEv = fCntTrkX = fCntaoz = fCntPtl = fCntTrk3D = 0;
} // end ctor

ETFAnalyzer::~ETFAnalyzer(){
  if(fpidm) delete fpidm; // fPID(m) is deleted within
  if(ftm) delete ftm;
  if(fdm) delete fdm;
  if(fVis) delete fVis;
  if(fAuxAna) delete fAuxAna;
} // end dtor

void ETFAnalyzer::Init(){
  fpm = ETFParaManager::Instance();
  fRun = ETFRun::Instance();
  fSrc = fRun->GetSource();
  if((*fpm->GetMisPara())["ispidm"])
    fIsPIDm = (*fpm->GetMisPara())["ispidm"].as<bool>();
  fpm->ConfigAnalyzer(this);
  fAuxAna = new ETFAuxAna();

  fdm = ETFDetectorManager::Instance();
  fdm->Configure(); // construct the dets
  if(fDetDebug) fdm->SetDebug(); // for printing channels upon assignment piecemeal
  if(fAnaLevel >= 1){
    ftm = ETFTrackManager::Instance();
    if(ftm->GetPostMagTracker()) fPhiAvrg = ftm->GetPostMagTracker()->GetPhiAvrg();
    if(fTrackingDebug) ftm->SetDebug();
  } // end if
  if(fAnaLevel >= 2){
    fpidm = ETFPIDManager::Instance(); // fpidm is the PID Manager
    fPID = fpidm->GetPID(); fPID->Configure();
    fPIDm = fpidm->GetPIDm(); fPIDm->Configure(); // fPIDm is the minor PID-er
    fpidm->SetMagneticIntensity(fpm->MagneticIntensity());
  } // end if
  // online has its own GUI drawing system
  if(!fRun->IsOnline()) fVis = ETFVisual::Instance();

  // get the raw event objects //
  const auto &vu = fSrc->upks(); // the vector of unpackers
  if(!vu.size()) ETFMsg::Error("ETFAnalyzer", "Init: no unpackers found");
  for(const auto &u : vu){
    if(u->pxi()) fPXISections.push_back(dynamic_cast<ETFPXIUnpacker *>(u)->GetCurrentEvent());
    else fVMEEvents.push_back(dynamic_cast<ETFVMEUnpacker *>(u)->GetCurrentEvent());
  } // end for
} // end member function Init

// register the mapped data and recognized tracks in the output tree
void ETFAnalyzer::Register(){
  fdm->Register();
  if(fAnaLevel >= 0 && fAuxAna){
    fAuxAna->Init();
    fAuxAna->Register();
  } // end if
  if(fAnaLevel >= 1) ftm->Register();
  if(fAnaLevel >= 2) fpidm->Register();
} // end member function Register

void ETFAnalyzer::Analyze(){
  static const bool isoffline = !fRun->IsOnline();
  static const bool vis = fDrawVec.size();

  if(fPXISections.size()) fIndex = fPXISections[0]->GetEventIndex();
  else if(fVMEEvents.size()) fIndex = fVMEEvents[0]->GetEventIndex();
  fCntEv++;

  // if(fAnaLevel < 0) return; // only unpacking
  // assign the raw data to detetors --- ANALYSIS STAGE 1 //
  fdm->Reset(); // clear the last event
  fdm->SetEventIndex(fIndex);
  // initiate the assignment (identification) of all the channels //
  for(auto &p : fPXISections) if(p->UnpackGood()){
    for(TObject *b : *p->tchs()) fdm->Assign(static_cast<ETFRawChannel *>(b));
  } // end for over pxi sections
  for(auto &v : fVMEEvents){
    for(TObject *b : *v->tchs()) fdm->Assign(static_cast<ETFRawChannel *>(b));
    for(TObject *b : *v->achs()) fdm->Assign(static_cast<ETFRawChannel *>(b));
  } // end for over vme events
  fdm->PostAssign(); // analyze the event as a whole
  if(fDetDebug){ fdm->Print(); getchar(); }
  if(fAuxAna) fAuxAna->Ana(); // further ana, e.g., secondary beam PID

  // do the tracking --- ANALYSIS STAGE 2 //
  if(fAnaLevel >= 1){
    ftm->Reset();
    ftm->Map();
    fCntTrkX += ftm->GetNofTracks();
    // do the 3D tracking --- ANALYSIS STAGE * //
    if(fIs3DTracking){
      ftm->Map3D();
      fCntTrk3D += ftm->GetNof3DTracks();
    } // end if
    if(fTrackingDebug){ ftm->Print(); getchar(); }
  } // end if

  // do the PID --- ANALYSIS STAGE 3 //
  if(fAnaLevel >= 2) PID();

  // draw the event --- ANALYSIS STAGE ** //
  if(isoffline && vis){
    if(find(fDrawVec.begin(), fDrawVec.end(), fIndex) != fDrawVec.end())
      fVis->DrawEventSnap(fIndex);
  } // end if
} // end member function Exec

void ETFAnalyzer::PrintPerEvent(){
  // ON-SCREEN OUTPUT //
  static bool once = true;
  if(once){ // print the banner
    once = false;
    // prepare for the output of the dynamic status on the terminal //
    cout << std::left;
    cout << "         " << setw(10) << "index" << setw(10) << "nEv" << setw(10);
    cout << "trkX" << setw(10) << "naoz" << setw(10) << "trk3D" << endl;
  } // end if
  // print real-time info //
  cout << setw(10) << fIndex << setw(10) << fCntEv << setw(10) << fCntTrkX;
  cout << setw(10) << fCntaoz << setw(10) << fCntTrk3D << setw(10) << "\r" << flush;
} // end member function PrintPerEvent

void ETFAnalyzer::PrintPerEventOnline(){
  static unsigned cnt0, nEv, t0_0, t1_0; // cnt of last second, nof ev per spill
  static ETFOnlineSource *src = static_cast<ETFOnlineSource *>(fSrc);
  static ETFOnlineCache *cache = src->Cache();
  static time_t time0 = ETFMsg::ms(), time1 = time0;
  int dnEv = src->CntEv() - cnt0;
  const double dtm = ETFMsg::ms() - time0, dt = dtm / 1000.; // dt between 2 calling
  cnt0 = src->CntEv();
  int t0 = fVMEEvents[0]->t0() - t0_0;
  int t1 = fVMEEvents[0]->t1() - t1_0;
  nEv += dnEv;

	ios_base::fmtflags initial;
	initial = cout.setf(ios_base::fixed, ios_base::floatfield);
	cout.precision(2);

  cout << "idx: ";
  for(auto *u : src->upks()) cout << u->Index() << ' ';
  cout << ", nEv: " << nEv << ", dnEv/s: " << dnEv / dt;
  cout << ", T1/T0: " << t1 << "/" << t0;
  cout << " = " << t1*100./t0 << "%" << endl;
  cout << "Queue (" << ETFQueue::kMAX_ITEM_NUM << ") Loaded Status: ";
  for(const auto &p : cache->daqs()) cout << p->daq->queue()->NLoaded() << ' ';
  cout << endl;

  if(!dnEv && nEv){ // beam extraction ends
    nEv = 0;
    t0_0 = fVMEEvents[0]->t0();
    t1_0 = fVMEEvents[0]->t1();
    fpm->SetReset(); // disseminate the RESET message
    cout << "\nExtraction ends, beam cycle: \033[1m";
    cout << (ETFMsg::ms() - time1) / 1000. << "s\033[0m\n" << endl;
    time1 = ETFMsg::ms();
    // sleep(2);
    // static_cast<ETFOnlineSource *>(fSrc)->Clear();
  } // end if

	cout.setf(initial); // restore initial formatting state
  time0 = ETFMsg::ms(); // beam time cycle
} // end member function PrintPerEventOnline

void ETFAnalyzer::PID(){
  bool pidsuccess = false;

  // note that fPID is for trk+trk PIDing of heavy residue in RIB exps. //
  fPID->Reset();
  pidsuccess = fPID->Fly(); // fly~~~
  if(pidsuccess) fCntPtl++;
  if(fPIDDebug){ fPID->Print(); getchar(); }

  // while fPIDm is for piont+trk PIDing of light fragments (e.g., p, pion, deutron) //
  if(fIsPIDm){
    fPIDm->Reset();
    pidsuccess = pidsuccess || fPIDm->Fly();
    if(fPIDDebug){ fPIDm->Print(); getchar(); }
  } // end if

  fCntaoz += pidsuccess;
  fCntPtl += fPIDm->GetNofParticles();
}// end member function PID

void ETFAnalyzer::PrintSummary(){
  cout << "  Totally \033[32;1m" << fCntEv;
  cout << "\033[0m events were analyzed, where we totally get \033[32;1m" << fCntTrkX;
  cout << "\033[0m X tracks and \033[32;1m" << fCntTrk3D << "\033[0m 3D Tracks, ";
  cout << "where \033[32;1m" << fCntaoz << "\033[0m events have \033[36;1m" << fCntPtl;
  cout << "\033[0m PIDed particles." << endl;
} // end member function PrintSummary
